home *** CD-ROM | disk | FTP | other *** search
/ Giga Games 1 / Giga Games.iso / net / go / comp / fumiko.sh < prev    next >
Encoding:
Linux/UNIX/POSIX Shell Script  |  1993-06-20  |  83.9 KB  |  2,361 lines

  1. #! /bin/sh
  2. # This is a shell archive, meaning:
  3. # 1. Remove everything above the #! /bin/sh line.
  4. # 2. Save the resulting text in a file.
  5. # 3. Execute the file with /bin/sh (not csh) to create:
  6. #    arcana.pas
  7. #    brdhndlr.pas
  8. #    butler.pas
  9. #    fumiko.doc
  10. #    fumiko.pas
  11. #    globlctv.pas
  12. #    groupmgr.pas
  13. #    sequence.pas
  14. #    tactics.pas
  15. #    trickle.fum
  16. # This archive created: Mon Aug 10 20:12:28 1992
  17. export PATH; PATH=/bin:/usr/bin:$PATH
  18. if test -f 'arcana.pas'
  19. then
  20.     echo shar: "will not over-write existing file 'arcana.pas'"
  21. else
  22. cat << \SHAR_EOF > 'arcana.pas'
  23. UNIT Arcana;
  24.   {Silly but necessary routines for Fumiko}
  25.  
  26.  
  27. INTERFACE
  28.  
  29.  
  30.   USES
  31.     Dos, GloblCTV;
  32.  
  33.  
  34.   CONST
  35.     point_interpretation_size = sizeof (point_interpretation);
  36.  
  37.  
  38.   FUNCTION Plenty_Of_Time (var turn: integer): boolean;
  39.     {Returns true if the game is running on or ahead of schedule}
  40.  
  41.  
  42.   FUNCTION On_Board (var x, y: byte): boolean;
  43.     {Returns true if the coordinates name a point on the board}
  44.  
  45.  
  46.   FUNCTION Screen_Char (var point: color_type): char;
  47.     {Returns the character to be printed in the board diagram for the point}
  48.  
  49.  
  50.   FUNCTION Opposite (var point: color_type): color_type;
  51.     {Black_stone and white_stone are opposites}
  52.  
  53.  
  54.   FUNCTION On_Edge (var x: byte): boolean;
  55.     {Returns true if the coordinate is at the edge of the board}
  56.  
  57.  
  58.   FUNCTION Line (var x: byte): byte;
  59.     {Returns the distance to the nearest edge}
  60.  
  61.  
  62.   FUNCTION Equal (var a, b; size: word): boolean;
  63.     {Compares two records.  From TP60 Programmer's Guide, p. 107}
  64.  
  65.  
  66.   PROCEDURE Compress (var removed: byte; var board: board_type);
  67.     {Puts the last item in the list into the hole}
  68.  
  69.  
  70.   PROCEDURE Find_Member (var group: group_type; var member: location_type);
  71.     {Sets member to the coordinates of a member of the group}
  72.  
  73.  
  74.   FUNCTION Highest_Rated (var rating: integer_board): location_list;
  75.     {Generates a list of the search_breadth highest rated locations, with}
  76.     {the highest-rated ones at the bottom}
  77.  
  78.  
  79.   PROCEDURE Clean_Out_Location_List (var list: location_list);
  80.     {Deallocates the list}
  81.  
  82.  
  83.   FUNCTION Count_Groups (var board: board_type): byte;
  84.     {Debugging function--count the number of groups}
  85.  
  86.  
  87. IMPLEMENTATION
  88.  
  89.  
  90.   TYPE
  91.     bytes = array [0..maxint] of byte;  {Used in equal}
  92.  
  93.  
  94.   FUNCTION Plenty_Of_Time (var turn: integer): boolean;
  95.     {Returns true if the game is running on or ahead of schedule}
  96.     Begin  {Plenty Of Time}
  97.       plenty_of_time := (100 * (125 - (turn div 2)) div time_left) < (12500 div total_time)
  98.     End;  {Plenty Of Time}
  99.  
  100.  
  101.   FUNCTION On_Board (var x, y: byte): boolean;
  102.     {Returns true if the coordinates name a point on the board}
  103.     Begin  {On Board}
  104.       on_board := (x > 0) and (x <= board_size) and (y > 0) and (y <= board_size)
  105.     End;  {On Board}
  106.  
  107.  
  108.   FUNCTION Screen_Char (var point: color_type): char;
  109.     {Returns the character to be printed in the board diagram for the point}
  110.     Begin  {Screen Char}
  111.       Case point of
  112.         black_stone: screen_char := #1;
  113.         white_stone: screen_char := #2;
  114.         empty: screen_char := #250;
  115.         off_board: screen_char := '?'
  116.       End
  117.     End;  {Screen Char}
  118.  
  119.  
  120.   FUNCTION Opposite (var point: color_type): color_type;
  121.     {black_stone and white_stone are opposites}
  122.     Begin  {Opposite}
  123.       If point = black_stone
  124.         Then opposite := white_stone
  125.         Else opposite := black_stone
  126.     End;  {Opposite}
  127.  
  128.  
  129.   FUNCTION On_Edge (var x: byte): boolean;
  130.     {Returns true if the coordinate is at the edge of the board}
  131.     Begin  {On Edge}
  132.       on_edge := (x = 1) or (x = board_size)
  133.     End;  {On Edge}
  134.  
  135.  
  136.   FUNCTION Line (var x: byte): byte;
  137.     {Returns the distance to the nearest edge}
  138.     Begin  {Line}
  139.       If x > (board_size + 1 - x)
  140.         Then line := board_size + 1 - x
  141.         Else line := x
  142.     End;  {Line}
  143.  
  144.  
  145.   FUNCTION Equal (var a, b; size: word): boolean;
  146.     {Compares two records.  From TP60 Programmer's Guide, p. 107}
  147.     Var
  148.       n: integer;
  149.     Begin  {Equal}
  150.       n := 0;
  151.       While (n < size) and (bytes (a) [n] = bytes (b) [n]) do
  152.         inc (n);
  153.       equal := n = size
  154.     End;  {Equal}
  155.  
  156.  
  157.   PROCEDURE Compress (var removed: byte; var board: board_type);
  158.     {Puts the last item in the list into the hole}
  159.     Var
  160.       last_group, x, y: byte;
  161.       buffer: group_pointer;
  162.     Begin  {Compress}
  163.       last_group := removed;
  164.       While board.groups [last_group + 1]^.size <> 0 do
  165.         Inc (last_group);
  166.       If last_group <> removed
  167.         Then Begin
  168.                For x := 1 to board_size do
  169.                  For y := 1 to board_size do
  170.                    With board.groups [last_group]^.interpretation [x, y] do
  171.                      If member
  172.                        Then board.data [x, y].group := removed;
  173.                buffer := board.groups [removed];
  174.                board.groups [removed] := board.groups [last_group];
  175.                board.groups [last_group] := buffer;
  176.                board.groups [last_group]^.size := 0
  177.              End
  178.         Else board.groups [removed]^.size := 0
  179.     End;  {Compress}
  180.  
  181.  
  182.   PROCEDURE Find_Member (var group: group_type; var member: location_type);
  183.     {Sets member to the coordinates of a member of the group}
  184.     Var
  185.       x, y: byte;
  186.     Begin  {Find Member}
  187.       x := 1;
  188.       y := 1;
  189.       member.x := 0;
  190.       Repeat
  191.         If group.interpretation [x, y].member
  192.           Then Begin
  193.                  member.x := x;
  194.                  member.y := y
  195.                End;
  196.         Inc (x);
  197.         If x > board_size
  198.           Then Begin
  199.                  x := 1;
  200.                  Inc (y)
  201.                End
  202.       Until member.x <> 0
  203.     End;  {Find Member}
  204.  
  205.  
  206.   PROCEDURE Swap_Values (var node_1, node_2: location_list);
  207.     {Swaps the location and value fields of the two nodes.  LOCAL}
  208.     Var
  209.       location_buffer: location_type;
  210.       value_buffer: integer;
  211.     Begin  {Swap Values}
  212.       location_buffer := node_1^.location;
  213.       value_buffer := node_1^.value;
  214.       node_1^.location := node_2^.location;
  215.       node_1^.value := node_2^.value;
  216.       node_2^.location := location_buffer;
  217.       node_2^.value := value_buffer
  218.     End;  {Swap Values}
  219.  
  220.  
  221.   PROCEDURE Filter_Down (var list: location_list; var x, y: byte; var value: integer);
  222.     {The ugly part of highest_rated.  Moves the new item along the list}
  223.     {until it meets a higher-value item.  LOCAL}
  224.     Var
  225.       this, that: location_list;
  226.       swapped: boolean;
  227.     Begin  {Filter Down}
  228.       New (this);
  229.       this^.location.x := x;
  230.       this^.location.y := y;
  231.       this^.value := value;
  232.       this^.next := nil;
  233.       that := list;
  234.       Repeat
  235.         If this^.value > that^.value
  236.           Then Begin
  237.                  Swap_Values (this, that);
  238.                  If this^.next = nil
  239.                    Then Dispose (this);
  240.                  this := that;
  241.                  that := that^.next;
  242.                  swapped := true
  243.                End
  244.           Else swapped := false
  245.       Until (that = nil) or (not swapped);
  246.       If that = list  {The new item did not make the list}
  247.         Then Dispose (this)
  248.     End;  {Filter Down}
  249.  
  250.  
  251.   FUNCTION Highest_Rated (var rating: integer_board): location_list;
  252.     {Generates a list of the search_breadth highest rated locations, with}
  253.     {the highest-rated ones at the bottom}
  254.     Var
  255.       x, y: byte;
  256.       best_moves, this_move, next_move: location_list;
  257.     Begin  {Highest Rated}
  258.       best_moves := nil;
  259.       For x := 1 to search_breadth do
  260.         Begin
  261.           New (this_move);
  262.           this_move^.value := 0;
  263.           this_move^.next := best_moves;
  264.           best_moves := this_move
  265.         End;
  266.       For x := 1 to board_size do
  267.         For y := 1 to board_size do
  268.           If rating [x, y] > 0
  269.             Then Filter_Down (best_moves, x, y, rating [x, y]);
  270.       this_move := best_moves;  {Get rid of any leftover empty moves}
  271.       While best_moves^.value = 0 do
  272.         best_moves := best_moves^.next;
  273.       While this_move <> best_moves do
  274.         Begin
  275.           next_move := this_move^.next;
  276.           Dispose (this_move);
  277.           this_move := next_move
  278.         End;
  279.       highest_rated := best_moves
  280.     End;  {Highest Rated}
  281.  
  282.  
  283.   PROCEDURE Clean_Out_Location_List (var list: location_list);
  284.     {Deallocates the list}
  285.     Var
  286.       next: location_list;
  287.     Begin  {Clean Out Location List}
  288.       While list <> nil do
  289.         Begin
  290.           next := list^.next;
  291.           Dispose (list);
  292.           list := next
  293.         End
  294.     End;  {Clean Out Location List}
  295.  
  296.  
  297.   FUNCTION Count_Groups (var board: board_type): byte;
  298.     {Debugging function--count the number of groups}
  299.     Var
  300.       tally: byte;
  301.     Begin  {Count Groups}
  302.       tally := 0;
  303.       While board.groups [tally + 1]^.size <> 0 do
  304.         Inc (tally);
  305.       count_groups := tally
  306.     End;  {Count Groups}
  307.  
  308.  
  309. END.  {Arcana}
  310. SHAR_EOF
  311. fi
  312. if test -f 'brdhndlr.pas'
  313. then
  314.     echo shar: "will not over-write existing file 'brdhndlr.pas'"
  315. else
  316. cat << \SHAR_EOF > 'brdhndlr.pas'
  317. UNIT BrdHndlr;
  318.   {Board handling routines for Fumiko}
  319.  
  320.  
  321. INTERFACE
  322.  
  323.  
  324.   USES
  325.     CRT, GloblCTV, Arcana;
  326.  
  327.  
  328.   FUNCTION Legal_Move (var x, y: byte; player: color_type; var board: board_type): boolean;
  329.     {Returns true if the move in question is legal}
  330.  
  331.  
  332.   FUNCTION Suicidal (var x, y: byte; var player: color_type; var board: board_type): boolean;
  333.     {Returns true if the move in question would result in its own capture}
  334.  
  335.  
  336.   PROCEDURE Draw_Board (var board: board_type);
  337.     {Draws the board on the screen}
  338.  
  339.  
  340.   PROCEDURE Get_User_Move (player: color_type; var board: board_type);
  341.     {Gets a move from the user and updates the board}
  342.  
  343.  
  344.   PROCEDURE Pass (var board: board_type);
  345.     {Increments the pass and turn counters and clears the ko variable}
  346.  
  347.  
  348.   PROCEDURE Play_At (var x, y: byte; var player: color_type; var board: board_type; depth: byte);
  349.     {Plays at the indicated place and updates the groups, passes,}
  350.     {turn, and ko variables}
  351.  
  352.  
  353.   PROCEDURE Estimate_Score (var board: board_type; var b, w: integer; var points: integer_board);
  354.     {Calculates influence for each point and adds up the score}
  355.  
  356.  
  357.   PROCEDURE Show_Score (var board: board_type);
  358.     {Estimates the score and shows the influence around the board}
  359.  
  360.  
  361.   PROCEDURE Save_Board (var board: board_type; var filename: string);
  362.     {Saves the board to a file and increments board_number}
  363.  
  364.  
  365.   PROCEDURE Load_Board (var board: board_type; var filename: string);
  366.     {Loads board from a file}
  367.  
  368.  
  369.   PROCEDURE Delete_Board (var filename: string);
  370.     {Deletes a saved board}
  371.  
  372.  
  373.   PROCEDURE Show_Final_Score (var board: board_type);
  374.     {Removes groups of aliveness 1-2, counts the score, and displays it}
  375.  
  376.  
  377. IMPLEMENTATION
  378.  
  379.  
  380.   USES
  381.     GroupMgr, Sequence;
  382.  
  383.  
  384.   FUNCTION A_Neighboring_Point_Is_Unoccupied (var x, y: byte; var board: board_type): boolean;
  385.     {Returns true if any adjacent point is empty.  LOCAL}
  386.     Var
  387.       direction: direction_type;
  388.       neighbor: location_type;
  389.       found_one: boolean;
  390.     Begin  {A Neighboring Point Is Unoccupied}
  391.       found_one := false;
  392.       For direction := north to west do
  393.         Begin
  394.           neighbor := neighbors [x, y, direction];
  395.           If on_board (neighbor.x, neighbor.y) and (board.data [neighbor.x, neighbor.y].color = empty)
  396.             Then found_one := true
  397.         End;
  398.       a_neighboring_point_is_unoccupied := found_one
  399.     End;  {A Neighboring Point Is Unoccupied}
  400.  
  401.  
  402.   FUNCTION Legal_Move (var x, y: byte; player: color_type; var board: board_type): boolean;
  403.     {Returns true if the move in question is legal}
  404.     Begin  {Legal Move}
  405.       If board.data [x, y].color <> empty
  406.         Then legal_move := false
  407.         Else If a_neighboring_point_is_unoccupied (x, y, board)
  408.                Then legal_move := true
  409.                Else legal_move := not ((board.ko.x = x) and (board.ko.y = y))
  410.     End;  {Legal Move}
  411.  
  412.  
  413.   FUNCTION Suicidal (var x, y: byte; var player: color_type; var board: board_type): boolean;
  414.     {Returns true if the move in question would result in its own capture}
  415.     Var
  416.       direction: direction_type;
  417.       safe: boolean;
  418.       adjacent_point: location_type;
  419.     Begin  {Suicidal}
  420.       If a_neighboring_point_is_unoccupied (x, y, board)
  421.         Then safe := true
  422.         Else Begin
  423.                safe := false;
  424.                For direction := north to west do
  425.                  Begin
  426.                    adjacent_point := neighbors [x, y, direction];
  427.                    If on_board (adjacent_point.x, adjacent_point.y)
  428.                      Then If board.data [adjacent_point.x, adjacent_point.y].color = player
  429.                             Then Begin
  430.                                    If board.groups [board.data [adjacent_point.x, adjacent_point.y].group]^.liberties > 1
  431.                                      Then safe := true
  432.                                  End
  433.                             Else If (board.data [adjacent_point.x, adjacent_point.y].color in [black_stone, white_stone])
  434.                                       and (board.groups [board.data [adjacent_point.x, adjacent_point.y].group]^.liberties = 1)
  435.                                    Then safe := true
  436.                  End
  437.              End;
  438.       suicidal := not safe
  439.     End;  {Suicidal}
  440.  
  441.  
  442.   PROCEDURE Draw_Board (var board: board_type);
  443.     {Draws the board on the screen}
  444.     Var
  445.       x, y: byte;
  446.       c: char;
  447.     Begin  {Draw Board}
  448.       ClrScr;
  449.       WriteLn (board_index_line);
  450.       For y := board_size downto 1 do
  451.         Begin
  452.           Write (y:2, ' ');
  453.           For x := 1 to board_size do
  454.             Begin
  455.               c := screen_char (board.data [x, y].color);
  456.               If (c = #250) and (x in [4, 10, 16]) and (y in [4, 10, 16])
  457.                 Then c := '+';
  458.               Write (c, ' ')
  459.             End;
  460.           WriteLn (y)
  461.         End;
  462.       WriteLn (board_index_line);
  463.       WriteLn
  464.     End;  {Draw Board}
  465.  
  466.  
  467.   PROCEDURE Show_Influence (var group: group_type);
  468.     {Displays the status of a group.  LOCAL}
  469.     Var
  470.       x, y: byte;
  471.     Begin  {Show Influence}
  472.       ClrScr;
  473.       WriteLn (board_index_line);
  474.       For y := board_size downto 1 do
  475.         Begin
  476.           Write (y:2, ' ');
  477.           For x := 1 to board_size do
  478.             If group.interpretation [x, y].too_distant
  479.               Then Write ('. ')
  480.               Else If group.interpretation [x, y].connected
  481.                      Then Write ('@ ')
  482.                      Else Write ('+ ');
  483.           WriteLn (y)
  484.         End;
  485.       WriteLn (board_index_line);
  486.       WriteLn;
  487.       WriteLn ('Size: ', group.size, '   Liberties: ', group.liberties);
  488.       WriteLn ('Aliveness: ', group.aliveness, '   Room: ', group.room);
  489.       WriteLn ('To kill: ', group.to_kill.x, ',', group.to_kill.y, '   To save: ', group.to_save.x, ',', group.to_save.y);
  490.       WriteLn ('(Return to continue)');
  491.       ReadLn
  492.     End;  {Show Influence}
  493.  
  494.  
  495.   PROCEDURE Show_Points_To_Attack (var group: group_type; var board: board_type);
  496.     {Performs and displays results of find_points_to_attack.  LOCAL}
  497.     Var
  498.       result: boolean_board;
  499.       x, y: byte;
  500.       points, here: location_list;
  501.     Begin  {Show Points To Attack}
  502.       points := critical_points_for (group, board);
  503.       ClrScr;
  504.       WriteLn ('Points to attack that group: ');
  505.       WriteLn;
  506.       Draw_Board (board);
  507.       here := points;
  508.       While here <> nil do
  509.         Begin
  510.           GotoXY (2 + (2 * here^.location.x), 2 + board_size - here^.location.y);
  511.           Write ('x');
  512.           here := here^.next
  513.         End;
  514.       GotoXY (1, 4 + board_size);
  515.       WriteLn ('(Return to continue)');
  516.       Clean_Out_Location_List (points);
  517.       ReadLn
  518.     End;  {Show Points To Attack}
  519.  
  520.  
  521.   PROCEDURE Get_User_Move (player: color_type; var board: board_type);
  522.     {Gets a move from the user and updates the board}
  523.     Var
  524.       move: string;
  525.       x, y: byte;
  526.       error_code: integer;
  527.       legal: boolean;
  528.       command: char;
  529.     Begin  {Get User Move}
  530.       Repeat
  531.         Draw_Board (board);
  532.         Write ('Turn #', board.turn, '   ');
  533.         If player = black_stone
  534.           Then Write ('Black (*) to move: ')
  535.           Else Write ('White (O) to move: ');
  536.         ReadLn (move);
  537.         legal := true;
  538.         command := ' ';
  539.         If move <> 'pass'
  540.           Then Begin
  541.                  If move [1] in ['@', '!', '?', '<']
  542.                    Then Begin
  543.                           legal := false;
  544.                           command := move [1];
  545.                           move := copy (move, 2, 3)
  546.                         End;
  547.                  Case upcase (move [1]) of
  548.                    'A'..'H': x := Ord (upcase (move [1])) - Ord ('A') + 1;
  549.                    'J'..'T': x := Ord (upcase (move [1])) - Ord ('A')
  550.                    Else legal := false
  551.                  End;
  552.                  Val (copy (move, 2, 2), y, error_code);
  553.                  If legal
  554.                    Then legal := (error_code = 0) and (on_board (x, y));
  555.                  If legal
  556.                    Then legal := legal_move (x, y, player, board)
  557.                End;
  558.         If not legal
  559.           Then If command <> ' '
  560.                  Then Begin
  561.                         If (on_board (x, y) and (board.data [x, y].group <> 0)) or (command in ['?', '<'])
  562.                           Then Case command of
  563.                                  '@': Show_Influence (board.groups [board.data [x, y].group]^);
  564.                                  '!': Show_Points_To_Attack (board.groups [board.data [x, y].group]^, board);
  565.                                  '?': Begin
  566.                                         WriteLn (strategy (board, player));
  567.                                         WriteLn ('(Return to continue)');
  568.                                         ReadLn
  569.                                       End;
  570.                                  '<': Begin
  571.                                         Load_Board (board, last_board_filename);
  572.                                         Draw_Board (board)
  573.                                       End
  574.                                End
  575.                       End
  576.                  Else Begin
  577.                         Write ('That is not a legal move -- return to continue');
  578.                         ReadLn
  579.                       End
  580.       Until legal;
  581.       Save_Board (physical_board, last_board_filename);
  582.       If move = 'pass'
  583.         Then Pass (board)
  584.         Else Play_At (x, y, player, board, 0)
  585.     End;  {Get User Move}
  586.  
  587.  
  588.   PROCEDURE Pass (var board: board_type);
  589.     {Increments the pass and turn counters and clears the ko variable}
  590.     Begin  {Pass}
  591.       Inc (board.passes);
  592.       Inc (board.turn);
  593.       board.ko.x := 0;
  594.       board.ko.y := 0
  595.     End;  {Pass}
  596.  
  597.  
  598.   PROCEDURE Update_Relevant_Groups (var board: board_type; var changed: boolean_board; var depth: byte);
  599.     {An ugly part of play_at.  Updates all groups who do not consider all of}
  600.     {the affected points too_distant.  LOCAL}
  601.     Var
  602.       color: color_type;
  603.       group, x, y: byte;
  604.     Begin  {Update Relevant Groups}
  605.       group := 1;
  606.       While board.groups [group]^.size <> 0 do
  607.         Begin
  608.           For x := 1 to board_size do
  609.             For y := 1 to board_size do
  610.               If changed [x, y] and (not board.groups [group]^.interpretation [x, y].too_distant)
  611.                    and (board.groups [group]^.last_update < board.turn)
  612.             Then Update_Interpretation (group, board, depth);
  613.           Inc (group)
  614.         End
  615.     End;  {Update Relevant Groups}
  616.  
  617.  
  618.   PROCEDURE Play_At (var x, y: byte; var player: color_type; var board: board_type; depth: byte);
  619.     {Plays at the indicated place and updates the groups, passes,}
  620.     {turns, and ko variables}
  621.     Var
  622.       direction: direction_type;
  623.       neighbor: location_type;
  624.       stones_captured: byte;
  625.       changes: boolean_board;
  626.       trickle: text;
  627.     Begin  {Play At}
  628.       board.data [x, y].color := player;
  629.       stones_captured := 0;
  630.       Make_New_Group (x, y, board);
  631.       changes := false_board;
  632.       changes [x, y] := true;
  633.       For direction := north to west do
  634.         Begin
  635.           neighbor := neighbors [x, y, direction];
  636.           If on_board (neighbor.x, neighbor.y)
  637.             Then Begin
  638.                    If board.data [neighbor.x, neighbor.y].color = player
  639.                      Then Begin
  640.                             If board.data [x, y].group <> board.data [neighbor.x, neighbor.y].group
  641.                               Then Merge_Groups (board.data [x, y].group, board.data [neighbor.x, neighbor.y].group, board)
  642.                           End
  643.                      Else If board.data [neighbor.x, neighbor.y].color in [black_stone, white_stone]
  644.                             Then Begin  {It's occupied by the other player}
  645.                                    Update_Interpretation (board.data [neighbor.x, neighbor.y].group, board, depth);
  646.                                    If board.groups [board.data [neighbor.x, neighbor.y].group]^.liberties = 0
  647.                                      Then Begin
  648.                                             board.ko := neighbor;
  649.                                             Inc (stones_captured,
  650.                                                   board.groups [board.data [neighbor.x, neighbor.y].group]^.size);
  651.                                             Capture (board.data [neighbor.x, neighbor.y].group, board, changes, depth)
  652.                                           End
  653.                                  End
  654.                  End
  655.         End;
  656.       Update_Interpretation (board.data [x, y].group, board, depth);
  657.       If (stones_captured = 0) and (board.groups [board.data [x, y].group]^.liberties = 0)
  658.         Then Capture (board.data [x, y].group, board, changes, depth)
  659.         Else If (stones_captured <> 1) or (board.groups [board.data [x, y].group]^.size <> 1)
  660.                   or (board.groups [board.data [x, y].group]^.liberties <> 1)
  661.                Then Begin
  662.                       board.ko.x := 0;
  663.                       board.ko.y := 0
  664.                     End;
  665.       Update_Relevant_Groups (board, changes, depth);
  666.       board.passes := 0;
  667.       If depth = 0
  668.         Then Begin
  669.                Assign (trickle, trickle_filename);
  670.                Append (trickle);
  671.                WriteLn (trickle, '+', board.turn);
  672.                WriteLn (trickle, x);
  673.                WriteLn (trickle, y);
  674.                Close (trickle)
  675.              End;
  676.       Inc (board.turn)
  677.     End;  {Play At}
  678.  
  679.  
  680.   PROCEDURE Estimate_Score (var board: board_type; var b, w: integer; var points: integer_board);
  681.     {Calculates influence for each point and adds up the score}
  682.     Var
  683.       x, y, group: byte;
  684.       influence: integer;
  685.       color: color_type;
  686.     Begin  {Estimate Score}
  687.       b := 0;
  688.       w := 0;
  689.       For x := 1 to board_size do
  690.         For y := 1 to board_size do
  691.           Begin
  692.             points [x, y] := 0;
  693.             group := 1;
  694.             While board.groups [group]^.size <> 0 do
  695.               Begin
  696.                 If board.groups [group]^.interpretation [x, y].connected
  697.                   Then Begin
  698.                          If board.groups [group]^.interpretation [x, y].member
  699.                            Then influence := board.groups [group]^.aliveness * 200
  700.                            Else influence := (board.groups [group]^.aliveness * 100)
  701.                                                div board.groups [group]^.interpretation [x, y].distance;
  702.                          If board.groups [group]^.owner = black_stone
  703.                            Then points [x, y] := points [x, y] + influence
  704.                            Else points [x, y] := points [x, y] - influence
  705.                        End;
  706.                 Inc (group)
  707.               End;
  708.             If points [x, y] > 0
  709.               Then b := b + 1
  710.               Else If points [x, y] < 0
  711.                      Then w := w + 1
  712.           End
  713.     End;  {Estimate Score}
  714.  
  715.  
  716.   PROCEDURE Show_Score (var board: board_type);
  717.     {Estimates the score and shows the influence around the board}
  718.     Var
  719.       x, y: byte;
  720.       b, w: integer;
  721.       points: integer_board;
  722.     Begin  {Show Score}
  723.       Estimate_Score (board, b, w, points);
  724.       ClrScr;
  725.       WriteLn (board_index_line);
  726.       For y := board_size downto 1 do
  727.         Begin
  728.           Write (y:2, ' ');
  729.           For x := 1 to board_size do
  730.             Case points [x, y] of
  731.               -maxint..-1000: Write ('W ');
  732.               -999..-100: Write ('w ');
  733.               -99..-1: Write ('- ');
  734.               0: Write ('. ');
  735.               1..99: Write ('| ');
  736.               100..999: Write ('b ');
  737.               1000..maxint: Write ('B ')
  738.             End;
  739.           WriteLn (y)
  740.         End;
  741.       WriteLn (board_index_line);
  742.       WriteLn;
  743.       WriteLn ('Black: ', b, '  White: ', w);
  744.       ReadLn
  745.     End;  {Show Score}
  746.  
  747.  
  748.   PROCEDURE Save_Board (var board: board_type; var filename: string);
  749.     {Saves the board to a file and increments board_number}
  750.     Var
  751.       board_file: board_file_type;
  752.       group_file: group_file_type;
  753.       count: byte;
  754.     Begin  {Save Board}
  755.       If Copy (filename, 3, 8) <> 'PHYSICAL'
  756.         Then Begin
  757.                Str (board_number, filename);
  758.                filename := drive_name + 'F' + filename;
  759.                board_number := (board_number + 1) mod maxint
  760.              End;
  761.       Assign (board_file, filename + '.BRD');
  762.       Rewrite (board_file);
  763.       Write (board_file, board);
  764.       Close (board_file);
  765.       Assign (group_file, filename + '.GRP');
  766.       Rewrite (group_file);
  767.       count := 1;
  768.       While (count <= max_groups) and (board.groups [count]^.size <> 0) do
  769.         Begin
  770.           Write (group_file, board.groups [count]^);
  771.           Inc (count)
  772.         End;
  773.       Close (group_file)
  774.     End;  {Save Board}
  775.  
  776.  
  777.   PROCEDURE Load_Board (var board: board_type; var filename: string);
  778.     {Loads board from a file}
  779.     Var
  780.       board_file: board_file_type;
  781.       group_file: group_file_type;
  782.       count: byte;
  783.     Begin  {Load Board}
  784.       Assign (board_file, filename + '.BRD');
  785.       {$I-}
  786.       Reset (board_file);
  787.       {$I+}
  788.       If ioresult = 0
  789.         Then Begin
  790.                Read (board_file, board);
  791.                Close (board_file);
  792.                Assign (group_file, filename + '.GRP');
  793.                Reset (group_file);
  794.                count := 1;
  795.                While not eof (group_file) do
  796.                  Begin
  797.                    Read (group_file, board.groups [count]^);
  798.                    Inc (count)
  799.                  End;
  800.                Close (group_file);
  801.                If (count <= max_groups)
  802.                  Then board.groups [count]^.size := 0
  803.              End
  804.     End;  {Load Board}
  805.  
  806.  
  807.   PROCEDURE Delete_Board (var filename: string);
  808.     {Deletes a saved board}
  809.     Var
  810.       deadfile: file;
  811.     Begin  {Delete Board}
  812.       Assign (deadfile, filename + '.BRD');
  813.       Erase (deadfile);
  814.       Assign (deadfile, filename + '.GRP');
  815.       Erase (deadfile)
  816.     End;  {Delete Board}
  817.  
  818.  
  819.   PROCEDURE Show_Final_Score (var board: board_type);
  820.     {Removes groups of aliveness 1-2, counts the score, and displays it}
  821.     Var
  822.       count, changes, x, y, black_neighbors, white_neighbors: byte;
  823.       counted: boolean_board;
  824.       black_score, white_score: real;
  825.       score_array: integer_board;
  826.       done: boolean;
  827.       direction: direction_type;
  828.       neighbor: location_type;
  829.     Begin  {Show Final Score}
  830.       count := 1;
  831.       time_left := maxint;
  832.       WriteLn ('Counting score...');
  833.       Repeat
  834.         changes := 0;
  835.         While board.groups [count]^.size <> 0 do
  836.           Begin
  837.             Update_Interpretation (count, board, 0);
  838.             If board.groups [count]^.aliveness < 3
  839.               Then Begin
  840.                      Capture (count, board, counted, 0);
  841.                      Inc (changes)
  842.                    End
  843.               Else Inc (count);
  844.             WriteLn ('Next: size ', board.groups [count]^.size)
  845.           End
  846.       Until changes = 0;
  847.       Draw_Board (board);
  848.       black_score := 0;
  849.       white_score := 0;
  850.       score_array := zero_board;
  851.       counted := false_board;
  852.       Repeat
  853.         For x := 1 to board_size do
  854.           For y := 1 to board_size do
  855.             If not counted [x, y]
  856.               Then Begin
  857.                      count := 0;
  858.                      If board.data [x, y].color in [black_stone, white_stone]
  859.                        Then If board.data [x, y].color = black_stone
  860.                               Then score_array [x, y] := 12
  861.                               Else score_array [x, y] := -12
  862.                        Else Begin
  863.                               white_neighbors := 0;
  864.                               black_neighbors := 0;
  865.                               For direction := north to west do
  866.                                 Begin
  867.                                   neighbor := neighbors [x, y, direction];
  868.                                   If on_board (neighbor.x, neighbor.y)
  869.                                     Then If board.data [neighbor.x, neighbor.y].color = black_stone
  870.                                            Then Inc (black_neighbors)
  871.                                            Else If board.data [neighbor.x, neighbor.y].color = white_stone
  872.                                                   Then Inc (white_neighbors)
  873.                                 End;
  874.                               Case (black_neighbors + white_neighbors) of
  875.                                 1: score_array [x, y] := (12 * black_neighbors) - (12 * white_neighbors);
  876.                                 2: score_array [x, y] := (6 * black_neighbors) - (6 * white_neighbors);
  877.                                 3: score_array [x, y] := (4 * black_neighbors) - (4 * white_neighbors);
  878.                                 4: score_array [x, y] := (3 * black_neighbors) - (3 * white_neighbors)
  879.                                 Else Begin
  880.                                        For direction := north to west do
  881.                                          Begin
  882.                                            neighbor := neighbors [x, y, direction];
  883.                                            If on_board (neighbor.x, neighbor.y)
  884.                                              Then Begin
  885.                                                     If counted [neighbor.x, neighbor.y]
  886.                                                       Then Inc (count);
  887.                                                     If score_array [neighbor.x, neighbor.y] > 0
  888.                                                       Then Inc (black_neighbors)
  889.                                                       Else If score_array [neighbor.x, neighbor.y] < 0
  890.                                                            Then Inc (white_neighbors)
  891.                                                   End
  892.                                          End;
  893.                                        If (black_neighbors > 0) and (white_neighbors > 0)
  894.                                          Then score_array [x, y] := 0
  895.                                          Else Case (black_neighbors + white_neighbors) of
  896.                                                 1: score_array [x, y] := (12 * black_neighbors) - (12 * white_neighbors);
  897.                                                 2: score_array [x, y] := (6 * black_neighbors) - (6 * white_neighbors);
  898.                                                 3: score_array [x, y] := (4 * black_neighbors) - (4 * white_neighbors);
  899.                                                 4: score_array [x, y] := (3 * black_neighbors) - (3 * white_neighbors)
  900.                                               End
  901.                                      End
  902.                               End
  903.                             End;
  904.                      counted [x, y] := (black_neighbors > 0) or (white_neighbors > 0);
  905.                      If counted [x, y]
  906.                        Then If score_array [x, y] > 0
  907.                               Then black_score := black_score + (score_array [x, y] / 12)
  908.                               Else white_score := white_score - (score_array [x, y] / 12)
  909.                        Else score_array [x, y] := 0
  910.                    End;
  911.         done := true;
  912.         For x := 1 to board_size do
  913.           For y := 1 to board_size do
  914.             If not counted [x, y]
  915.               Then done := false
  916.       Until done;
  917.       WriteLn;
  918.       WriteLn ('Black:  ', black_score:6:2, '  White: ', white_score:6:2)
  919.     End;  {Show Final Score}
  920.  
  921.  
  922. END.  {BrdHndlr}
  923. SHAR_EOF
  924. fi
  925. if test -f 'butler.pas'
  926. then
  927.     echo shar: "will not over-write existing file 'butler.pas'"
  928. else
  929. cat << \SHAR_EOF > 'butler.pas'
  930. UNIT Butler;
  931.   {Setup and cleanup procedures for Fumiko}
  932.  
  933.  
  934. INTERFACE
  935.  
  936.  
  937.   USES
  938.     Dos, Crt, GloblCTV, Arcana;
  939.  
  940.  
  941.   PROCEDURE Initialize;
  942.     {Initializes all of the variables at the beginning of a run}
  943.  
  944.  
  945. IMPLEMENTATION
  946.  
  947.  
  948.   USES
  949.     BrdHndlr;
  950.  
  951.  
  952.   PROCEDURE Find_Neighbor_Coordinates (var x, y: byte; var direction: direction_type; var neighbor: location_type);
  953.     {Finds the coordinates of a point's neighbor in the specified direction,}
  954.     {and returns (0, 0) if the result is off the board.  LOCAL}
  955.     Begin  {Find Neighbor Coordinates}
  956.       neighbor.x := x;
  957.       neighbor.y := y;
  958.       Case direction of
  959.         north: neighbor.y := y + 1;
  960.         south: neighbor.y := y - 1;
  961.         east: neighbor.x := x + 1;
  962.         west: neighbor.x := x - 1;
  963.         nw: Begin
  964.               neighbor.x := x - 1;
  965.               neighbor.y := y + 1
  966.             End;
  967.         sw: Begin
  968.               neighbor.x := x - 1;
  969.               neighbor.y := y - 1
  970.             End;
  971.         se: Begin
  972.               neighbor.x := x + 1;
  973.               neighbor.y := y - 1
  974.             End;
  975.         ne: Begin
  976.               neighbor.x := x + 1;
  977.               neighbor.y := y + 1
  978.             End
  979.       End;
  980.       If not on_board (neighbor.x, neighbor.y)
  981.         Then Begin
  982.                neighbor.x := 0;
  983.                neighbor.y := 0
  984.              End
  985.     End;  {Find Neighbor Coordinates}
  986.  
  987.  
  988.   PROCEDURE Build_Neighbor_Array;
  989.     {Builds the neighboring-coordinates array.  LOCAL}
  990.     Var
  991.       x, y: byte;
  992.       direction: direction_type;
  993.     Begin  {Build Neighbor Array}
  994.       For x := 1 to board_size do
  995.         For y := 1 to board_size do
  996.           For direction := north to ne do
  997.             Begin
  998.               Find_Neighbor_Coordinates (x, y, direction, neighbors [x, y, direction]);
  999.               If not on_board (neighbors [x, y, direction].x, neighbors [x, y, direction].y)
  1000.                 Then Begin
  1001.                        neighbors [x, y, direction].x := 0;
  1002.                        neighbors [x, y, direction].y := 0
  1003.                      End
  1004.             End;
  1005.       For direction := north to ne do
  1006.         Begin
  1007.           neighbors [0, 0, direction].x := 0;
  1008.           neighbors [0, 0, direction].y := 0
  1009.         End
  1010.     End;  {Build Neighbor Array}
  1011.  
  1012.  
  1013.   PROCEDURE Clear_Off (var board: board_type);
  1014.     {Sets all board points to empty, group pointers to nil.  LOCAL}
  1015.     Var
  1016.       x, y: byte;
  1017.     Begin  {Clear Off}
  1018.       With board do
  1019.         Begin
  1020.           For x := 1 to board_size do
  1021.             For y := 1 to board_size do
  1022.               With data [x, y] do
  1023.                 Begin
  1024.                   color := empty;
  1025.                   group := 0;
  1026.                 End;
  1027.           For x := 1 to max_groups do
  1028.             Begin
  1029.               New (groups [x]);
  1030.               groups [x]^.size := 0;
  1031.               groups [x]^.to_kill.x := 0;
  1032.               groups [x]^.to_kill.y := 0;
  1033.               groups [x]^.to_save.x := 0;
  1034.               groups [x]^.to_save.y := 0
  1035.             End;
  1036.           ko.x := 0;
  1037.           ko.y := 0;
  1038.           passes := 0;
  1039.           turn := 1
  1040.         End
  1041.     End;  {Clear Off}
  1042.  
  1043.  
  1044.   PROCEDURE Set_Terrain (var terrain: integer_board);
  1045.     Var
  1046.       x, y: byte;
  1047.     Begin  {Set Terrain}
  1048.       terrain := zero_board;
  1049.       For x := 1 to board_size do
  1050.         For y := 1 to board_size do
  1051.           Begin
  1052.             Case line (x) of
  1053.               2, 8..board_size: Inc (terrain [x, y], 1000);
  1054.               3, 4: Inc (terrain [x, y], 5000);
  1055.               5..7: Inc (terrain [x, y], 5000 - (1000 * (line (x) - 4)))
  1056.             End;
  1057.             Case line (y) of
  1058.               2, 8..board_size: Inc (terrain [x, y], 1000);
  1059.               3, 4: Inc (terrain [x, y], 5000);
  1060.               5..7: Inc (terrain [x, y], 5000 - (1000 * (line (y) - 4)))
  1061.             End;
  1062.           End
  1063.     End;  {Set Terrain}
  1064.  
  1065.  
  1066.   PROCEDURE Load_Trickle_File (var board: board_type);
  1067.     {Tournament kludge--loads and plays saved moves}
  1068.     Var
  1069.       trickle: text;
  1070.       x, y: byte;
  1071.       move_number: integer;
  1072.       player: color_type;
  1073.     Begin  {Load Trickle File}
  1074.       Assign (trickle, trickle_filename);
  1075.       Reset (trickle);
  1076.       While not eof (trickle) do
  1077.         Begin
  1078.           ReadLn (trickle, move_number);
  1079.           ReadLn (trickle, x);
  1080.           ReadLn (trickle, y);
  1081.           If odd (move_number)
  1082.             Then player := black_stone
  1083.             Else player := white_stone;
  1084.           Play_At (x, y, player, board, maximum_search_depth)
  1085.         End;
  1086.       Close (trickle)
  1087.     End;  {Load Trickle File}
  1088.  
  1089.  
  1090.   PROCEDURE Initialize;
  1091.     {Initializes all of the variables at the beginning of a run}
  1092.     Var
  1093.       x, y: byte;
  1094.       answer: string;
  1095.       trickle: text;
  1096.     Begin  {Initialize}
  1097.       ClrScr;
  1098.       WriteLn ('FUMIKO -- An artificially intelligent Go program');
  1099.       WriteLn;
  1100.       WriteLn;
  1101.       WriteLn ('"A path is formed by walking on it."');
  1102.       WriteLn ('                          -Chuang Tsu');
  1103.       WriteLn;
  1104.       WriteLn;
  1105.       Write ('How many minutes will each player get? ');
  1106.       ReadLn (total_time);
  1107.       total_time := total_time * 60;
  1108.       time_left := total_time;
  1109.       Build_Neighbor_Array;
  1110.       Clear_Off (physical_board);
  1111.       board_index_line := '   ';
  1112.       For x := 1 to board_size do
  1113.         If x < 9
  1114.           Then board_index_line := board_index_line + Chr (x + Ord ('A') - 1) + ' '
  1115.           Else board_index_line := board_index_line + Chr (x + Ord ('A')) + ' ';
  1116.       For x := 1 to board_size do
  1117.         For y := 1 to board_size do
  1118.           Begin
  1119.             false_board [x, y] := false;
  1120.             zero_board [x, y] := 0
  1121.           End;
  1122.       Set_Terrain (terrain);
  1123.       Write ('Enter name of board drive (for example, "e:"): ');
  1124.       ReadLn (drive_name);
  1125.       last_board_filename := drive_name + 'PHYSICAL';
  1126.       trickle_filename := drive_name + 'TRICKLE.FUM';
  1127.       board_number := 1;
  1128.       WriteLN ('New game, saved board, or trickle (n/s/t)? ');
  1129.       ReadLn (answer);
  1130.       Case upcase (answer [1]) of
  1131.         'N': Begin
  1132.                Assign (trickle, trickle_filename);
  1133.                Rewrite (trickle);
  1134.                Close (trickle)
  1135.              End;
  1136.         'S': Load_Board (physical_board, last_board_filename);
  1137.         'T': Load_Trickle_File (physical_board);
  1138.       End;
  1139.       Write ('What color should I play (b/w)? ');
  1140.       ReadLn (answer);
  1141.       If upcase (answer [1]) = 'W'
  1142.         Then my_color := white_stone
  1143.         Else my_color := black_stone;
  1144.       If not odd (physical_board.turn)
  1145.         Then next_player := white_stone
  1146.         Else next_player := black_stone;
  1147.       Draw_Board (physical_board)
  1148.     End;  {Initialize}
  1149.  
  1150.  
  1151. END.  {Butler}
  1152.  
  1153. SHAR_EOF
  1154. fi
  1155. if test -f 'fumiko.doc'
  1156. then
  1157.     echo shar: "will not over-write existing file 'fumiko.doc'"
  1158. else
  1159. cat << \SHAR_EOF > 'fumiko.doc'
  1160. This is my first fully-functional go program, entitled Fumiko.  I am
  1161. abandoning it and beginning a new one, to be entitled "HellGo".  I wish
  1162. to make the .exe and .pas code available for everyone to browse, play with,
  1163. and learn from.
  1164.  
  1165. I don't know much about FTP-ing, so could you please copy the following files
  1166. up to the directory there?  They are in /home/jupiter/student/pdudey, and
  1167. should be readable.
  1168.  
  1169. SOURCE CODE
  1170.   globlctv.pas
  1171.   arcana.pas
  1172.   butler.pas
  1173.   groupmgr.pas
  1174.   brdhndlr.pas
  1175.   tactics.pas
  1176.   sequence.pas
  1177.   fumiko.pas  (The main program)
  1178.  
  1179. EXECUTABLE CODE  (IBM compatible)
  1180.   fumiko.exe
  1181.  
  1182. NECESSARY SPARE FILE
  1183.   trickle.fum
  1184.  
  1185.   That last one isn't actually -necessary-, but the program needs to have an
  1186. extant file of that name.
  1187.  
  1188.   A WORD OF WARNING:  When doing lookahead, Fumiko writes to disk quite often.
  1189. If you can install a ramdrive of 1 meg or bigger, do it!
  1190.  
  1191.   The program is written in Turbo Pascal 6.0.
  1192.  
  1193. ------------------------------------------------------------------------------
  1194. When you run the program, she (black = yin = female) will ask how much time
  1195. each player gets.  She's supposed to panic when she gets low on time;  that
  1196. feature doesn't work perfectly, but I'm starting on a new program and don't
  1197. want to mess with it;  if you care, you've got the source code!  (Look in
  1198. arcana and sequence, I think).
  1199.  
  1200. A saved game must consist of two files:  physical.brd and physical.grp.
  1201. Fumiko saves the game every turn.
  1202.  
  1203. Loading from the trickle file replays all of the moves that have been played.
  1204. You can edit this file (to correct typos &c)--each move is a turn number
  1205. (preceded by a '+'), and row, and a column.
  1206.  
  1207. At the 'to move' prompt, type coordinates--e.g., 'b13', or 'pass'.
  1208.  
  1209. You can also type '<' to back up.  THIS WILL MESS UP THE TRICKLE FILE.
  1210.  
  1211. Typing @b6 will give you the status of the group at b6.  Typing !b6 will
  1212. suggest points to attack it.  Typing ? will suggest a strategy.
  1213.  
  1214. I think that does it.  Email me at pdudey@willamette.edu if you have any
  1215. questions, comments, or suggestions.  I plan to make HellGo public when it
  1216. is done, too;  I'm working on a grant, so I don't have to keep my stuff
  1217. secret!
  1218.  
  1219. SHAR_EOF
  1220. fi
  1221. if test -f 'fumiko.pas'
  1222. then
  1223.     echo shar: "will not over-write existing file 'fumiko.pas'"
  1224. else
  1225. cat << \SHAR_EOF > 'fumiko.pas'
  1226. PROGRAM Fumiko;
  1227.   {Artificially intelligent, learning Go program}
  1228.  
  1229.  
  1230. USES
  1231.   GloblCTV, Arcana, Butler, GroupMgr, BrdHndlr, Sequence;
  1232.  
  1233.  
  1234. BEGIN  {Fumiko}
  1235.   Initialize;
  1236.   Repeat
  1237.     If next_player = my_color
  1238.       Then Play_Move (physical_board, my_color, 0)
  1239.       Else Begin
  1240.              Get_User_Move (next_player, physical_board);
  1241.              Draw_Board (physical_board)
  1242.            End;
  1243.     next_player := opposite (next_player)
  1244.   Until physical_board.passes >= 2;
  1245.   WriteLn;
  1246.   Write ('(Return to remove dead groups)');
  1247.   ReadLn;
  1248.   Show_Final_Score (physical_board);
  1249.   WriteLn;
  1250.   Delete_Board (last_board_filename);
  1251.   Write ('(Return to go back to DOS)');
  1252.   ReadLn
  1253. END.  {Fumiko}
  1254.  
  1255. SHAR_EOF
  1256. fi
  1257. if test -f 'globlctv.pas'
  1258. then
  1259.     echo shar: "will not over-write existing file 'globlctv.pas'"
  1260. else
  1261. cat << \SHAR_EOF > 'globlctv.pas'
  1262. UNIT GloblCTV;
  1263.   {Global constants, types, and variables for Fumiko}
  1264.  
  1265.  
  1266. INTERFACE
  1267.  
  1268.  
  1269.   CONST
  1270.  
  1271.     board_size = 19;
  1272.  
  1273.     board_size_squared = board_size * board_size;
  1274.  
  1275.     maximum_search_depth = 4;
  1276.  
  1277.     search_breadth = 3;
  1278.  
  1279.     max_groups = 180;
  1280.  
  1281.  
  1282.   TYPE
  1283.  
  1284.     direction_type = (north, south, east, west, nw, sw, se, ne);
  1285.  
  1286.     color_type = (black_stone, white_stone, empty, off_board);  {'black' and 'white' are used by the CRT unit}
  1287.  
  1288.     location_type = record
  1289.                       x, y: byte
  1290.                     end;
  1291.  
  1292.     location_list = ^location_list_node;
  1293.  
  1294.     location_list_node = record
  1295.                            value: integer;
  1296.                            location: location_type;
  1297.                            next: location_list
  1298.                          end;
  1299.  
  1300.     boolean_board = array [1..board_size, 1..board_size] of boolean;
  1301.  
  1302.     integer_board = array [1..board_size, 1..board_size] of integer;
  1303.  
  1304.     point_interpretation = record
  1305.                              too_distant, member, adjacent, connected: boolean;
  1306.                              distance: byte
  1307.                            end;
  1308.  
  1309.     board_interpretation = array [1..board_size, 1..board_size] of point_interpretation;
  1310.  
  1311.     group_pointer = ^group_type;
  1312.  
  1313.     group_type = record
  1314.                    size, liberties, aliveness, room: byte;
  1315.                    last_update: integer;
  1316.                    owner: color_type;
  1317.                    interpretation: board_interpretation;
  1318.                    to_kill, to_save: location_type
  1319.                  end;
  1320.  
  1321.     board_space = record
  1322.                     color: color_type;
  1323.                     group: byte
  1324.                   end;
  1325.  
  1326.     board_type = record
  1327.                    data: array [1..board_size, 1..board_size] of board_space;  {0, 0 is an off_board space}
  1328.                    ko: location_type;
  1329.                    passes: byte;
  1330.                    turn: integer;
  1331.                    groups: array [1..max_groups] of group_pointer
  1332.                  end;
  1333.  
  1334.     board_file_type = file of board_type;
  1335.  
  1336.     group_file_type = file of group_type;
  1337.  
  1338.  
  1339.   VAR
  1340.     physical_board: board_type;
  1341.     neighbors: array [0..board_size, 0..board_size, direction_type] of location_type;
  1342.     false_board: boolean_board;
  1343.     board_index_line: string;
  1344.     board_number: longint;
  1345.     my_color: color_type;
  1346.     zero_board, terrain: integer_board;
  1347.     total_time, time_left: word;
  1348.     last_board_filename, drive_name, trickle_filename: string;
  1349.     next_player: color_type;
  1350.  
  1351.  
  1352. IMPLEMENTATION
  1353.  
  1354. END.  {GloblCTV}
  1355.  
  1356. SHAR_EOF
  1357. fi
  1358. if test -f 'groupmgr.pas'
  1359. then
  1360.     echo shar: "will not over-write existing file 'groupmgr.pas'"
  1361. else
  1362. cat << \SHAR_EOF > 'groupmgr.pas'
  1363. UNIT GroupMgr;
  1364.   {Group manager for Fumiko}
  1365.  
  1366.  
  1367. INTERFACE
  1368.  
  1369.  
  1370.   USES
  1371.     CRT, GloblCTV, Arcana, Butler;
  1372.  
  1373.  
  1374.   PROCEDURE Make_New_Group (var x, y: byte; var board: board_type);
  1375.     {Builds a new group containing one stone, in terms of size and members}
  1376.     {only, and adds it to the group list}
  1377.  
  1378.  
  1379.   PROCEDURE Merge_Groups (a, b: byte; var board: board_type);
  1380.     {Merges two groups, in terms of size and members only,}
  1381.     {and removes group b from the group list}
  1382.  
  1383.  
  1384.   FUNCTION Maneuvering_Room (var x, y: byte; var owner: color_type; var board: board_type): boolean;
  1385.     {Returns true if the point is not controlled by the other player}
  1386.  
  1387.  
  1388.   PROCEDURE Update_Interpretation (var group: byte; var board: board_type; depth: byte);
  1389.     {Updates a group's interpretation of its surroundings}
  1390.  
  1391.  
  1392.   PROCEDURE Capture  (group: byte; var board: board_type; var changes: boolean_board; depth: byte);
  1393.     {Removes the offending stones from the board and their group from the list}
  1394.     {Also updates the liberties of nearby groups}
  1395.  
  1396.  
  1397. IMPLEMENTATION
  1398.  
  1399.  
  1400.   USES
  1401.     Sequence;
  1402.  
  1403.  
  1404.   PROCEDURE Make_New_Group (var x, y: byte; var board: board_type);
  1405.     {Builds a new group containing one stone, in terms of size and members}
  1406.     {only, and adds it to the group list}
  1407.     Var
  1408.       a, b: byte;
  1409.     Begin  {Make New Group}
  1410.       board.data [x, y].group := 1;
  1411.       While board.groups [board.data [x, y].group]^.size <> 0 do
  1412.         Inc (board.data [x, y].group);
  1413.       With board.groups [board.data [x, y].group]^ do
  1414.         Begin
  1415.           size := 1;
  1416.           aliveness := 4;
  1417.           room := 0;
  1418.           last_update := 0;
  1419.           owner := board.data [x, y].color;
  1420.           to_kill.x := 0;
  1421.           to_kill.y := 0;
  1422.           For a := 1 to board_size do  {List members in minimal interpretation}
  1423.             For b := 1 to board_size do
  1424.               interpretation [a, b].member := false;
  1425.           interpretation [x, y].member := true
  1426.         End;
  1427.       board.groups [board.data [x, y].group + 1]^.size := 0
  1428.     End;  {Make New Group}
  1429.  
  1430.  
  1431.   PROCEDURE Merge_Groups (a, b: byte; var board: board_type);
  1432.     {Merges two groups, in terms of size and members only,}
  1433.     {and removes group b from the group list}
  1434.     Var
  1435.       x, y: byte;
  1436.     Begin  {Merge Groups}
  1437.       board.groups [a]^.size := board.groups [a]^.size + board.groups [b]^.size;
  1438.       If board.groups [b]^.aliveness > board.groups [a]^.aliveness  {The new group has the stronger of the two lives}
  1439.         Then board.groups [a]^.aliveness := board.groups [b]^.aliveness;
  1440.       If board.groups [b]^.last_update < board.groups [a]^.last_update  {The new group has the least recent update}
  1441.         Then board.groups [a]^.last_update := board.groups [b]^.last_update;
  1442.       For x := 1 to board_size do
  1443.         For y := 1 to board_size do
  1444.           If board.groups [a]^.interpretation [x, y].member or board.groups [b]^.interpretation [x, y].member
  1445.             Then Begin
  1446.                    board.groups [a]^.interpretation [x, y].member := true;
  1447.                    board.data [x, y].group := a
  1448.                  End;
  1449.       Compress (b, board)
  1450.     End;  {Merge Groups}
  1451.  
  1452.  
  1453.   FUNCTION Maneuvering_Room (var x, y: byte; var owner: color_type; var board: board_type): boolean;
  1454.     {Returns true if the point is not controlled by the other player}
  1455.     Var
  1456.       direction: direction_type;
  1457.       theirs: byte;
  1458.       neighbor: location_type;
  1459.     Begin  {Maneuvering Room}
  1460.       If (board.data [x, y].color = empty) or
  1461.            ((board.data [x, y].color = opposite (owner)) and (board.groups [board.data [x, y].group]^.aliveness < 3))
  1462.         Then Begin
  1463.                theirs := 0;
  1464.                For direction := north to ne do
  1465.                  Begin
  1466.                    neighbor := neighbors [x, y, direction];
  1467.                    If on_board (neighbor.x, neighbor.y)
  1468.                      Then With board.data [neighbors [x, y, direction].x, neighbors [x, y, direction].y] do
  1469.                             If (color = opposite (owner)) and (board.groups [group]^.aliveness > 2)
  1470.                               Then If direction in [north..west]
  1471.                                      Then Inc (theirs, 2)
  1472.                                      Else Inc (theirs, 1)
  1473.                  End;
  1474.                maneuvering_room := (theirs = 0) or ((theirs = 1) and (not on_edge (x)) and (not on_edge (y)))
  1475.              End
  1476.         Else maneuvering_room := false
  1477.     End;  {Maneuvering Room}
  1478.  
  1479.  
  1480.   PROCEDURE Consider_Point (var x, y: byte; var group: group_type; var interpretation: point_interpretation;
  1481.                              var board: board_type; var distance: byte);
  1482.     {The ugly part of look_around.  Updates the interpretation of a}
  1483.     {particular point.  LOCAL}
  1484.     Begin  {Consider Point}
  1485.       If interpretation.too_distant
  1486.         Then Begin  {The group has influence here--analyze it}
  1487.                interpretation.too_distant := false;
  1488.                interpretation.distance := distance;
  1489.                If board.data [x, y].color = empty
  1490.                  Then Begin
  1491.                         If maneuvering_room (x, y, group.owner, board)
  1492.                           Then Begin
  1493.                                  interpretation.connected := true;
  1494.                                  Inc (group.room);
  1495.                                End
  1496.                       End
  1497.                  Else If board.data [x, y].color = group.owner
  1498.                         Then Begin
  1499.                                interpretation.connected := true;
  1500.                                If board.groups [board.data [x, y].group]^.aliveness > group.aliveness
  1501.                                  Then group.aliveness := board.groups [board.data [x, y].group]^.aliveness
  1502.                                  Else board.groups [board.data [x, y].group]^.aliveness := group.aliveness
  1503.                              End
  1504.                         Else If maneuvering_room (x, y, group.owner, board)
  1505.                                Then Begin
  1506.                                       interpretation.connected := true;
  1507.                                       Inc (group.room)
  1508.                                     End
  1509.                End
  1510.     End;  {Consider Point}
  1511.  
  1512.  
  1513.   PROCEDURE Look_Around (var group: group_type; distance, previous_differences: byte; var board: board_type);
  1514.     {The recursive part of update_interpretation.  Looks at points}
  1515.     {around those not labelled too_distant.  LOCAL}
  1516.     Var
  1517.       x, y, differences: byte;
  1518.       new_interpretation: ^board_interpretation;
  1519.       direction: direction_type;
  1520.       point: location_type;
  1521.     Begin  {Look Around}
  1522.       New (new_interpretation);
  1523.       new_interpretation^ := group.interpretation;
  1524.       For x := 1 to board_size do
  1525.         For y := 1 to board_size do
  1526.           If (group.interpretation [x, y].connected) and not
  1527.                ((board.data [x, y].group <> 0) and (board.groups [board.data [x, y].group]^.aliveness > 3)
  1528.                   and (board.data [x, y].color = opposite (group.owner)))
  1529.             Then For direction := north to west do
  1530.                    Begin
  1531.                      point := neighbors [x, y, direction];
  1532.                      If on_board (point.x, point.y)
  1533.                        Then Consider_Point (point.x, point.y, group, new_interpretation^ [point.x, point.y], board, distance);
  1534.                    End;
  1535.       differences := 0;
  1536.       For x := 1 to board_size do
  1537.         For y := 1 to board_size do
  1538.           Begin
  1539.             If not equal (group.interpretation [x, y], new_interpretation^ [x, y], point_interpretation_size)
  1540.               Then Inc (differences);
  1541.             group.interpretation [x, y] := new_interpretation^ [x, y]
  1542.           End;
  1543.       Dispose (new_interpretation);
  1544.       If (distance < group.aliveness) and (differences > previous_differences)
  1545.         Then Look_Around (group, distance + 1, differences, board)
  1546.     End;  {Look Around}
  1547.  
  1548.  
  1549.   PROCEDURE Clear_Out (var interpretation: board_interpretation);
  1550.     {Sets all interpretation bits to false, except member (which is}
  1551.     {unaffected) and too_distant (which is := not member).  LOCAL}
  1552.     Var
  1553.       x, y: byte;
  1554.     Begin  {Clear Out}
  1555.       For x := 1 to board_size do
  1556.         For y := 1 to board_size do
  1557.           With interpretation [x, y] do
  1558.             Begin
  1559.               too_distant := not member;
  1560.               adjacent := false;
  1561.               connected := false;
  1562.               distance := 255
  1563.             End
  1564.     End;  {Clear Out}
  1565.  
  1566.  
  1567.   PROCEDURE Consider_Adjacent_Point (var x, y: byte; var group: group_type; var board: board_type);
  1568.     {The ugly part of update_interpretation.  Updates the interpretation of}
  1569.     {a particular adjacent point.  LOCAL}
  1570.     Begin  {Consider Adjacent Point}
  1571.       If (on_board (x, y)) and (group.interpretation [x, y].too_distant)
  1572.         Then With group.interpretation [x, y] do
  1573.                Begin
  1574.                  too_distant := false;
  1575.                  adjacent := true;
  1576.                  group.interpretation [x, y].distance := 1;
  1577.                  If board.data [x, y].color = empty
  1578.                    Then Begin
  1579.                           If maneuvering_room (x, y, group.owner, board)
  1580.                             Then Begin
  1581.                                    Inc (group.room);
  1582.                                    connected := true
  1583.                                  End;
  1584.                           Inc (group.liberties)
  1585.                         End
  1586.                    Else If board.data [x, y].color in [black_stone, white_stone]
  1587.                              {It must be occupied by the opponent}
  1588.                           Then Begin
  1589.                                  If maneuvering_room (x, y, group.owner, board)
  1590.                                    Then Begin
  1591.                                           connected := true;
  1592.                                           Inc (group.room)
  1593.                                         End
  1594.                                End
  1595.                End
  1596.     End;  {Consider Adjacent Point}
  1597.  
  1598.  
  1599.   PROCEDURE Update_Interpretation (var group: byte; var board: board_type; depth: byte);
  1600.     {Updates a group's interpretation of its surroundings}
  1601.     Var
  1602.       x, y: byte;
  1603.       direction: direction_type;
  1604.       neighbor: location_type;
  1605.     Begin  {Update Interpretation}
  1606.       With board.groups [group]^ do
  1607.         Begin
  1608.           liberties := 0;
  1609.           room := 0;
  1610.           last_update := board.turn;
  1611.           Clear_Out (interpretation);
  1612.           For x := 1 to board_size do
  1613.             For y := 1 to board_size do
  1614.               If interpretation [x, y].member
  1615.                 Then Begin
  1616.                        interpretation [x, y].connected := true;
  1617.                        interpretation [x, y].distance := 0;
  1618.                        For direction := north to west do
  1619.                          Begin
  1620.                            neighbor := neighbors [x, y, direction];
  1621.                            Consider_Adjacent_Point (neighbor.x, neighbor.y, board.groups [group]^, board);
  1622.                          End
  1623.                      End;
  1624.           If depth <= maximum_search_depth
  1625.             Then Begin
  1626.                    Look_Around (board.groups [group]^, 2, 0, board);
  1627.                    Update_Aliveness (board.groups [group]^, board, depth)
  1628.                  End
  1629.         End
  1630.     End;  {Update Interpretation}
  1631.  
  1632.  
  1633.   PROCEDURE Capture  (group: byte; var board: board_type; var changes: boolean_board; depth: byte);
  1634.     {Removes the offending stones from the board and their group from the list}
  1635.     {Also updates the liberties of nearby groups}
  1636.     Var
  1637.       x, y: byte;
  1638.     Begin  {Capture}
  1639.       For x := 1 to board_size do
  1640.         For y := 1 to board_size do
  1641.           If board.groups [group]^.interpretation [x, y].member
  1642.             Then Begin
  1643.                    changes [x, y] := true;
  1644.                    board.data [x, y].color := empty;
  1645.                    board.data [x, y].group := 0
  1646.                  End;
  1647.       Compress (group, board)
  1648.     End;  {Capture}
  1649.  
  1650.  
  1651. END.  {GroupMgr}
  1652.  
  1653. SHAR_EOF
  1654. fi
  1655. if test -f 'sequence.pas'
  1656. then
  1657.     echo shar: "will not over-write existing file 'sequence.pas'"
  1658. else
  1659. cat << \SHAR_EOF > 'sequence.pas'
  1660. UNIT Sequence;
  1661.   {Life and death and sequencing routines for Fumiko}
  1662.  
  1663.  
  1664. INTERFACE
  1665.  
  1666.  
  1667.   USES
  1668.     Dos, CRT, GloblCTV, Arcana, Butler;
  1669.  
  1670.  
  1671.   FUNCTION Alive (var group: group_type; var board: board_type; depth: byte): boolean;
  1672.     {Returns true if the group in question is unconditionally alive}
  1673.  
  1674.  
  1675.   FUNCTION Life (group: group_type; first_player: color_type; var board: board_type;
  1676.                   depth: byte; var crucial_move: location_type): byte;
  1677.     {The big one, folks!  Reads out sequences to see if the group in question}
  1678.     {can live.}
  1679.  
  1680.  
  1681.   PROCEDURE Update_Aliveness (var group: group_type; var board: board_type; var depth: byte);
  1682.     {Rates the group's life from 1 (hosed) to 7 (2 eyes)}
  1683.  
  1684.  
  1685.   FUNCTION Critical_Points_For (var group: group_type; var board: board_type): location_list;
  1686.     {Generates a list of moves to consider in attacking/defending the group}
  1687.  
  1688.  
  1689.   FUNCTION Strategy (var board: board_type; var player: color_type): string;
  1690.     {Returns a string suggesting territory, defense, or offense}
  1691.  
  1692.  
  1693.   PROCEDURE Pick_Move (var board: board_type; player: color_type; var move: location_list; depth: byte);
  1694.     {Picks a strategy and calls the appropriate tacticians}
  1695.  
  1696.  
  1697.   PROCEDURE Play_Move (var board: board_type; player: color_type; depth: byte);
  1698.     {Calls pick_move and plays it}
  1699.  
  1700.  
  1701. IMPLEMENTATION
  1702.  
  1703.  
  1704.   USES
  1705.     GroupMgr, BrdHndlr, Tactics;
  1706.  
  1707.  
  1708.   PROCEDURE Fill_Liberties (var x, y: byte; var liberty: boolean_board; var board: board_type;
  1709.                              var new_move_made: boolean; var depth: byte);
  1710.     {An ugly part of alive.  Plays opposing stones in liberties of the group}
  1711.     {in question.  LOCAL}
  1712.     Var
  1713.       a, b: byte;
  1714.       attacker: color_type;
  1715.     Begin  {Fill Liberties}
  1716.       new_move_made := false;
  1717.       attacker := opposite (board.groups [board.data [x, y].group]^.owner);
  1718.       For a := 1 to board_size do
  1719.         For b := 1 to board_size do
  1720.           If board.data [x, y].group <> 0
  1721.             Then Begin
  1722.                    If liberty [a, b]
  1723.                      Then If legal_move (a, b, attacker, board)
  1724.                                and not suicidal (a, b, attacker, board)
  1725.                             Then Begin
  1726.                                    liberty [a, b] := false;
  1727.                                    new_move_made := true;
  1728.                                    Play_At (a, b, attacker, board, 1 + maximum_search_depth)
  1729.                                  End
  1730.                  End
  1731.     End;  {Fill Liberties}
  1732.  
  1733.  
  1734.   PROCEDURE Add_Helpful_Groups (var group: byte; var liberty: boolean_board; var board: board_type; var addition: boolean);
  1735.     {Another ugly part of alive.  Finds groups that share liberties with the}
  1736.     {group in question, and add their liberties to the liberty array.  LOCAL}
  1737.     Var
  1738.       x, y, a, b: byte;
  1739.       direction: direction_type;
  1740.       neighbor: location_type;
  1741.     Begin  {Add Helpful Groups}
  1742.       addition := false;
  1743.       For x := 1 to board_size do
  1744.         For y := 1 to board_size do
  1745.           If liberty [x, y]
  1746.             Then Begin  {See if any other groups border on this liberty}
  1747.                    For direction := north to west do
  1748.                      Begin
  1749.                        neighbor := neighbors [x, y, direction];
  1750.                        If (on_board (neighbor.x, neighbor.y))
  1751.                             and (board.data [neighbor.x, neighbor.y].color = board.groups [group]^.owner)
  1752.                             and not (board.data [neighbor.x, neighbor.y].group = group)
  1753.                          Then Begin
  1754.                                 For a := 1 to board_size do
  1755.                                   For b := 1 to board_size do
  1756.                                     If board.groups [board.data [neighbor.x, neighbor.y].group]^
  1757.                                         .interpretation [a, b].adjacent and
  1758.                                         (board.data [neighbor.x, neighbor.y].color = empty)
  1759.                                       Then If not liberty [a, b]
  1760.                                              Then Begin
  1761.                                                     liberty [a, b] := true;
  1762.                                                     addition := true
  1763.                                                   End
  1764.                               End
  1765.                      End
  1766.                  End
  1767.     End;  {Add Helpful Groups}
  1768.  
  1769.  
  1770.   FUNCTION Alive (var group: group_type; var board: board_type; depth: byte): boolean;
  1771.     {Returns true if the group in question is unconditionally alive}
  1772.     Var
  1773.       x, y: byte;
  1774.       member: location_type;
  1775.       liberty: boolean_board;
  1776.       new_move_made, another_group_added: boolean;
  1777.       previous_board_file: string;
  1778.     Begin  {Alive}
  1779.       Write (depth, ' ');
  1780.       Save_Board (board, previous_board_file);
  1781.       For x := 1 to board_size do
  1782.         For y := 1 to board_size do
  1783.           liberty [x, y] := group.interpretation [x, y].adjacent
  1784.                              and (board.data [x, y].color = empty);
  1785.       Find_Member (group, member);
  1786.       Repeat
  1787.         Repeat
  1788.           Fill_Liberties (member.x, member.y, liberty, board, new_move_made, depth)
  1789.         Until (not new_move_made)
  1790.                 or (board.data [member.x, member.y].group = 0)
  1791.                 or (board.groups [board.data [member.x, member.y].group]^.liberties = 1);
  1792.         If (not new_move_made)
  1793.              and (board.data [member.x, member.y].group <> 0)
  1794.              and (board.groups [board.data [member.x, member.y].group]^.liberties > 1)
  1795.           Then Add_Helpful_Groups (board.data [member.x, member.y].group, liberty, board, another_group_added)
  1796.           Else another_group_added := false
  1797.       Until not another_group_added;
  1798.       alive := (board.data [member.x, member.y].group <> 0)
  1799.                  and (board.groups [board.data [member.x, member.y].group]^.liberties > 1);
  1800.       Load_Board (board, previous_board_file);
  1801.       Delete_Board (previous_board_file)
  1802.     End;  {Alive}
  1803.  
  1804.  
  1805.   FUNCTION Aji  (var group: group_type; var board: board_type): byte;
  1806.     {Performs a static analysis on a group's life.  LOCAL}
  1807.     Var
  1808.       x, y: byte;
  1809.       neighbor_in_atari: boolean;
  1810.     Begin  {Aji}
  1811.       If group.liberties = 1
  1812.         Then Begin
  1813.                neighbor_in_atari := false;
  1814.                For x := 1 to board_size do
  1815.                  For y := 1 to board_size do
  1816.                    With group.interpretation [x, y] do
  1817.                      If adjacent and (board.data [x, y].color = opposite (group.owner))
  1818.                          and (board.groups [board.data [x, y].group]^.liberties = 1)
  1819.                        Then neighbor_in_atari := true;
  1820.                If neighbor_in_atari
  1821.                  Then aji := 4  {Unknown}
  1822.                  Else aji := 1  {Hosed}
  1823.              End
  1824.         Else Case group.room of
  1825.                0..1: aji := 2;  {Probably hosed}
  1826.                10..255: aji := 6  {Probably alive}
  1827.                Else aji := 4  {Unknown}
  1828.              End
  1829.     End;  {Aji}
  1830.  
  1831.  
  1832.   FUNCTION Life (group: group_type; first_player: color_type; var board: board_type;
  1833.                   depth: byte; var crucial_move: location_type): byte;
  1834.     {The big one, folks!  Reads out sequences to see if the group in question}
  1835.     {can live.}
  1836.     Var
  1837.       points_to_consider, move: location_list;
  1838.       where: location_type;
  1839.       best: byte;
  1840.       defending, found_one: boolean;
  1841.       previous_board_file: string;
  1842.     Begin  {Life}
  1843.       If (depth >= maximum_search_depth) or (group.aliveness in [1, 6, 7])
  1844.         Then life := group.aliveness
  1845.         Else Begin
  1846.                defending := first_player = group.owner;
  1847.                found_one := false;
  1848.                points_to_consider := critical_points_for (group, board);
  1849.                Find_Member (group, where);
  1850.                move := points_to_consider;
  1851.                Save_Board (board, previous_board_file);
  1852.                While move <> nil do
  1853.                  If found_one or suicidal (move^.location.x, move^.location.y, first_player, board)
  1854.                    Then Begin
  1855.                           If defending
  1856.                             Then move^.value := 0
  1857.                             Else move^.value := 8;
  1858.                           move := move^.next
  1859.                         End
  1860.                    Else Begin
  1861.                           Play_At (move^.location.x, move^.location.y, first_player, board, depth + 1);
  1862.                           If board.data [where.x, where.y].group = 0
  1863.                             Then move^.value := 0
  1864.                             Else If plenty_of_time (physical_board.turn)
  1865.                                    Then move^.value := life (board.groups [board.data [where.x, where.y].group]^,
  1866.                                                               opposite (first_player), board, depth + 1, crucial_move)
  1867.                                    Else move^.value := life (board.groups [board.data [where.x, where.y].group]^,
  1868.                                                               opposite (first_player), board, depth + 2, crucial_move);
  1869.                           If (defending and (move^.value = 7))
  1870.                                or ((not defending) and (move^.value = 1))
  1871.                             Then found_one := true;
  1872.                           move := move^.next;
  1873.                           Load_Board (board, previous_board_file)
  1874.                         End;
  1875.                Delete_Board (previous_board_file);
  1876.                If defending
  1877.                  Then best := 0
  1878.                  Else best := 8;
  1879.                move := points_to_consider;
  1880.                While move <> nil do
  1881.                  Begin
  1882.                    If defending
  1883.                      Then Begin
  1884.                             If move^.value > best
  1885.                               Then Begin
  1886.                                      best := move^.value;
  1887.                                      crucial_move := move^.location
  1888.                                    End
  1889.                           End
  1890.                      Else If move^.value < best
  1891.                             Then Begin
  1892.                                    best := move^.value;
  1893.                                    crucial_move := move^.location
  1894.                                  End;
  1895.                    move := move^.next
  1896.                  End;
  1897.                Clean_Out_Location_List (points_to_consider);
  1898.                life := best
  1899.              End
  1900.     End;  {Life}
  1901.  
  1902.  
  1903.   PROCEDURE Update_Aliveness (var group: group_type; var board: board_type; var depth: byte);
  1904.     {Rates the group's life from 1 (hosed) to 7 (2 eyes)}
  1905.     Var
  1906.       opponent_first, self_first: byte;
  1907.     Begin  {Update Aliveness}
  1908.       With group do
  1909.         If aliveness < 7  {It's not already unconditionally alive}
  1910.           Then Begin
  1911.                  aliveness := aji (group, board);
  1912.                  If aliveness = 4  {Unsettled}
  1913.                    Then If alive (group, board, depth)
  1914.                           Then aliveness := 7  {Alive}
  1915.                           Else If (depth = 0) and (plenty_of_time (board.turn) or (group.last_update = 0))
  1916.                                    {Only read sequences from physical board, and when there's time}
  1917.                                  Then Begin
  1918.                                         opponent_first := life (group, opposite (group.owner), board, depth, group.to_kill);
  1919.                                         If opponent_first > 5
  1920.                                           Then aliveness := 5  {Alive if opponent moves first}
  1921.                                           Else Begin
  1922.                                                  self_first := life (group, group.owner, board, depth, group.to_save);
  1923.                                                  If opponent_first < 3
  1924.                                                    Then aliveness := aliveness - 1;
  1925.                                                  If self_first > 5
  1926.                                                    Then aliveness := aliveness + 1
  1927.                                                End
  1928.                                       End
  1929.                End
  1930.     End;  {Update Aliveness}
  1931.  
  1932.  
  1933.   FUNCTION Attack_Potential_At (var x, y: byte; var group: group_type; var board: board_type): integer;
  1934.     {Returns an integer rating of the point as a place to attack the group}
  1935.     {LOCAL}
  1936.     Var
  1937.       direction: direction_type;
  1938.       neighbor: location_type;
  1939.       importance, tally: integer;
  1940.     Begin  {Attack Potential At}
  1941.       If legal_move (x, y, opposite (group.owner), board)
  1942.         Then Begin
  1943.                If (group.interpretation [x, y].adjacent) and (board.data [x, y].color = empty)
  1944.                  Then tally := 5000
  1945.                  Else tally := 0;
  1946.                For direction := north to ne do
  1947.                  Begin
  1948.                    neighbor := neighbors [x, y, direction];
  1949.                    If on_board (neighbor.x, neighbor.y)
  1950.                      Then Begin
  1951.                             importance := 0;
  1952.                             With group.interpretation [neighbor.x, neighbor.y] do
  1953.                               If connected or adjacent
  1954.                                 Then If board.data [neighbor.x, neighbor.y].color = group.owner
  1955.                                        Then importance := 10
  1956.                                        Else If adjacent and (board.data [neighbor.x, neighbor.y].color = empty)
  1957.                                               Then importance := 5
  1958.                                               Else importance := 2;
  1959.                             If direction in [north..west]
  1960.                               Then importance := importance * 141
  1961.                               Else importance := importance * 100;
  1962.                             tally := tally + importance
  1963.                           End
  1964.                  End;
  1965.                attack_potential_at := tally
  1966.              End
  1967.         Else attack_potential_at := 0
  1968.     End;  {Attack Potential At}
  1969.  
  1970.  
  1971.   FUNCTION Critical_Points_For (var group: group_type; var board: board_type): location_list;
  1972.     {Generates a list of points to consider in attacking/defending the group}
  1973.     Var
  1974.       x, y: byte;
  1975.       rating: integer_board;
  1976.     Begin  {Critical Points For}
  1977.       For x := 1 to board_size do
  1978.         For y := 1 to board_size do
  1979.           rating [x, y] := Attack_Potential_At (x, y, group, board);
  1980.       critical_points_for := highest_rated (rating)
  1981.     End;  {Critical Points For}
  1982.  
  1983.  
  1984.   FUNCTION Strategy (var board: board_type; var player: color_type): string;
  1985.     {Returns a string suggesting territory, defense, or offense}
  1986.     Var
  1987.       b, w: integer;
  1988.       count: byte;
  1989.       points: integer_board;
  1990.       i_have_a_weak_group, he_has_a_weak_group: boolean;
  1991.     Begin  {Strategy}
  1992.       Estimate_Score (board, b, w, points);
  1993.       If b + w < board_size_squared div 2
  1994.         Then strategy := 'Grab for some of that empty territory'
  1995.         Else Begin
  1996.                i_have_a_weak_group := false;
  1997.                he_has_a_weak_group := false;
  1998.                count := 1;
  1999.                Repeat
  2000.                  If board.groups [count]^.aliveness in [3..5]
  2001.                    Then If board.groups [count]^.owner = player
  2002.                           Then i_have_a_weak_group := true
  2003.                           Else he_has_a_weak_group := true;
  2004.                  Inc (count)
  2005.                Until (board.groups [count]^.size = 0) or (i_have_a_weak_group and he_has_a_weak_group);
  2006.                If abs (w - b) < board_size
  2007.                  Then If i_have_a_weak_group
  2008.                         Then strategy := 'Defend your groups'
  2009.                         Else If he_has_a_weak_group
  2010.                                Then strategy := 'Attack the opponent''s groups'
  2011.                                Else strategy := 'It''s close--push'
  2012.                  Else If ((w > b) and (player = black_stone)) or ((b > w) and (player = white_stone))
  2013.                         Then If he_has_a_weak_group
  2014.                                Then strategy := 'You need to kill something'
  2015.                                Else strategy := 'You''re in trouble--invade'
  2016.                         Else If i_have_a_weak_group
  2017.                                Then strategy := 'Keep your lead--defend your groups'
  2018.                                Else strategy := 'You''re winning--seal it up'
  2019.              End
  2020.     End;  {Strategy}
  2021.  
  2022.  
  2023.   PROCEDURE Pick_Move (var board: board_type; player: color_type; var move: location_list; depth: byte);
  2024.     {Picks a strategy and calls the appropriate tacticians}
  2025.     Var
  2026.       count: byte;
  2027.       b, w: integer;
  2028.       points: integer_board;
  2029.       i_have_a_weak_group, he_has_a_weak_group: boolean;
  2030.     Begin  {Pick Move}
  2031.       move := nil;
  2032.       Estimate_Score (board, b, w, points);
  2033.       If b + w < board_size_squared div 2
  2034.         Then Grab_Territory (board, player, move, points, depth)
  2035.         Else Begin
  2036.                i_have_a_weak_group := false;
  2037.                he_has_a_weak_group := false;
  2038.                count := 1;
  2039.                Repeat
  2040.                  If board.groups [count]^.aliveness in [4..5]
  2041.                    Then If board.groups [count]^.owner = player
  2042.                           Then i_have_a_weak_group := true
  2043.                           Else he_has_a_weak_group := true;
  2044.                  Inc (count)
  2045.                Until (board.groups [count]^.size = 0) or (i_have_a_weak_group and he_has_a_weak_group);
  2046.                If abs (w - b) < board_size
  2047.                  Then If i_have_a_weak_group
  2048.                         Then Defend_Groups (board, player, move, depth)
  2049.                         Else If he_has_a_weak_group
  2050.                                Then Attack_Opponent (board, player, move, depth)
  2051.                                Else Push (board, player, move, points, depth)
  2052.                  Else If ((w > b) and (player = black_stone)) or ((b > w) and (player = white_stone))
  2053.                         Then If he_has_a_weak_group
  2054.                                Then Attack_Opponent (board, player, move, depth)
  2055.                                Else Invade (board, player, move, points, depth)
  2056.                         Else If i_have_a_weak_group
  2057.                                Then Defend_Groups (board, player, move, depth)
  2058.              End;
  2059.       If move = nil
  2060.         Then Seal_Up (board, player, move, points, depth);
  2061.       If move = nil
  2062.         Then Push (board, player, move, points, depth);
  2063.       If (move = nil) and (i_have_a_weak_group)
  2064.         Then Defend_Groups (board, player, move, depth);
  2065.       If (move = nil) and (he_has_a_weak_group)
  2066.         Then Attack_Opponent (board, player, move, depth);
  2067.       If move = nil
  2068.         Then Invade (board, player, move, points, depth);
  2069.       If move = nil
  2070.         Then Begin
  2071.                New (move);
  2072.                move^.location.x := 0;
  2073.                move^.location.y := 0;
  2074.                move^.next := nil
  2075.              End
  2076.     End;  {Pick Move}
  2077.  
  2078.  
  2079.   PROCEDURE Play_Move (var board: board_type; player: color_type; depth: byte);
  2080.     {Calls pick_move and plays it}
  2081.     Var
  2082.       move, good_moves: location_list;
  2083.       start_hour, start_minute, start_second, start_hundredths, hour, minute, second, hundredths: word;
  2084.       move_coordinates: string;
  2085.     Begin  {Play Move}
  2086.       WriteLn ('I have ', time_left div 60, ' minutes left');
  2087.       GetTime (start_hour, start_minute, start_second, start_hundredths);
  2088.       Pick_Move (board, player, good_moves, depth);
  2089.       move := good_moves;
  2090.       While move^.next <> nil do
  2091.         move := move^.next;
  2092.       If on_board (move^.location.x, move^.location.y)
  2093.         Then Begin
  2094.                Str (move^.location.y, move_coordinates);
  2095.                Case move^.location.x of
  2096.                  1..8: move_coordinates := chr (move^.location.x + ord ('a') - 1) + move_coordinates;
  2097.                  9..19: move_coordinates := chr (move^.location.x + ord ('a')) + move_coordinates
  2098.                End;
  2099.                WriteLn;
  2100.                WriteLn ('*** I move at ', move_coordinates, ' ***');
  2101.                Play_At (move^.location.x, move^.location.y, player, physical_board, 0)
  2102.              End
  2103.         Else If move^.location.y = 0
  2104.                Then Begin
  2105.                       WriteLn;
  2106.                       WriteLn ('*** I Pass ***');
  2107.                       Pass (board)
  2108.                     End
  2109.                Else Get_User_Move (player, board);
  2110.       WriteLn ('(Return to continue)');
  2111.       ReadLn;
  2112.       Clean_Out_Location_List (good_moves);
  2113.       GetTime (hour, minute, second, hundredths);
  2114.       If hour > start_hour
  2115.         Then Dec (time_left, (hour - start_hour) * 3600)
  2116.         Else If start_hour > hour
  2117.                Then Dec (time_left, (24 + hour - start_hour) * 3600);
  2118.       If minute > start_minute
  2119.         Then Dec (time_left, (minute - start_minute) * 60)
  2120.         Else If start_minute > minute
  2121.                Then Inc (time_left, (start_minute - minute) * 60);
  2122.       If second > start_second
  2123.         Then Dec (time_left, second - start_second)
  2124.         Else If start_second > second
  2125.                Then Inc (time_left, start_second - second);
  2126.       If (time_left > total_time) or (time_left = 0)
  2127.         Then time_left := 1
  2128.     End;  {Play Move}
  2129.  
  2130.  
  2131. END.  {Sequence}
  2132.  
  2133. SHAR_EOF
  2134. fi
  2135. if test -f 'tactics.pas'
  2136. then
  2137.     echo shar: "will not over-write existing file 'tactics.pas'"
  2138. else
  2139. cat << \SHAR_EOF > 'tactics.pas'
  2140. UNIT Tactics;
  2141.   {Tactical routines for Fumiko}
  2142.  
  2143.  
  2144. INTERFACE
  2145.  
  2146.  
  2147.   USES
  2148.     GloblCTV, Arcana, GroupMgr, BrdHndlr;
  2149.  
  2150.  
  2151.   PROCEDURE Grab_Territory (var board: board_type; player: color_type; var move: location_list;
  2152.                               var weights: integer_board; depth: byte);
  2153.     {Plays in wide-open areas}
  2154.  
  2155.  
  2156.   PROCEDURE Defend_Groups (var board: board_type; player: color_type; var move: location_list; depth: byte);
  2157.     {Defends weak groups}
  2158.  
  2159.  
  2160.   PROCEDURE Attack_Opponent (var board: board_type; player: color_type; var move: location_list; depth: byte);
  2161.     {Attacks weak groups}
  2162.  
  2163.  
  2164.   PROCEDURE Push (var board: board_type; player: color_type; var move: location_list; var weights: integer_board; depth: byte);
  2165.     {Plays on opponent's side of borders}
  2166.  
  2167.  
  2168.   PROCEDURE Invade (var board: board_type; player: color_type; var move: location_list;
  2169.                       var weights: integer_board; depth: byte);
  2170.     {Plays inside loose enemy territory}
  2171.  
  2172.  
  2173.   PROCEDURE Seal_Up (var board: board_type; player: color_type; var move: location_list;
  2174.                        var weights: integer_board; depth: byte);
  2175.     {Plays on own side of borders}
  2176.  
  2177.  
  2178. IMPLEMENTATION
  2179.  
  2180.  
  2181.   USES
  2182.     Sequence;
  2183.  
  2184.  
  2185.   FUNCTION Border (var x, y: byte; var weights: integer_board): boolean;
  2186.     {Returns true if the space is on the edge of an influence area.  LOCAL}
  2187.     Var
  2188.       direction: direction_type;
  2189.       neighbor: location_type;
  2190.       positive, negative: boolean;
  2191.     Begin  {Border}
  2192.       positive := false;
  2193.       negative := false;
  2194.       For direction := north to west do
  2195.         Begin
  2196.           neighbor := neighbors [x, y, direction];
  2197.           If on_board (neighbor.x, neighbor.y)
  2198.             Then Case weights [neighbor.x, neighbor.y] of
  2199.                    (-maxint - 1)..-1: negative := true;
  2200.                    0: Begin
  2201.                         positive := true;
  2202.                         negative := true
  2203.                       End;
  2204.                    1..maxint: positive := true
  2205.                  End
  2206.         End;
  2207.       border := negative and positive
  2208.     End;  {Border}
  2209.  
  2210.  
  2211.   PROCEDURE Grab_Territory (var board: board_type; player: color_type; var move: location_list;
  2212.                               var weights: integer_board; depth: byte);
  2213.     {Plays in wide-open areas}
  2214.     Var
  2215.       x, y: byte;
  2216.     Begin  {Grab Territory}
  2217.       WriteLn ('I''l take some of that territory...');
  2218.       For x := 1 to board_size do
  2219.         For y := 1 to board_size do
  2220.           If (weights [x, y] = 0) and legal_move (x, y, player, board) and not suicidal (x, y, player, board)
  2221.             Then weights [x, y] := 10000 + terrain [x, y]
  2222.             Else weights [x, y] := (10000 div abs (weights [x, y])) + terrain [x, y];
  2223.       move := highest_rated (weights)
  2224.     End;  {Grab Territory}
  2225.  
  2226.   PROCEDURE Defend_Groups (var board: board_type; player: color_type; var move: location_list; depth: byte);
  2227.     {Defends weak groups}
  2228.     Var
  2229.       value: integer_board;
  2230.       count, garbage: byte;
  2231.     Begin  {Defend Groups}
  2232.       WriteLn ('I''m a little nervous about these guys');
  2233.       value := zero_board;
  2234.       count := 1;
  2235.       While board.groups [count]^.size <> 0 do
  2236.         With board.groups [count]^ do
  2237.           Begin
  2238.             If (owner = player) and (aliveness in [3..5])
  2239.               Then If on_board (to_save.x, to_save.y) and legal_move (to_save.x, to_save.y, player, board)
  2240.                         and not suicidal (to_save.x, to_save.y, player, board)
  2241.                      Then Inc (value [to_save.x, to_save.y], size)
  2242.                      Else Begin
  2243.                             garbage := Life (board.groups [count]^, player, board, depth, to_save);
  2244.                             If on_board (to_save.x, to_save.y)
  2245.                               Then Inc (value [to_save.x, to_save.y], size)
  2246.                           End;
  2247.             Inc (count)
  2248.           End;
  2249.       move := highest_rated (value)
  2250.     End;  {Defend Groups}
  2251.  
  2252.  
  2253.   PROCEDURE Attack_Opponent (var board: board_type; player: color_type; var move: location_list; depth: byte);
  2254.     {Attacks weak groups}
  2255.     Var
  2256.       value: integer_board;
  2257.       count, garbage: byte;
  2258.     Begin  {Attack Opponent}
  2259.       WriteLn ('Die, Pink boy!');
  2260.       value := zero_board;
  2261.       count := 1;
  2262.       While board.groups [count]^.size <> 0 do
  2263.         With board.groups [count]^ do
  2264.           Begin
  2265.             If (owner = opposite (player)) and (aliveness in [3..5])
  2266.               Then If on_board (to_kill.x, to_kill.y) and legal_move (to_kill.x, to_kill.y, player, board)
  2267.                         and not suicidal (to_kill.x, to_kill.y, player, board)
  2268.                      Then Inc (value [to_kill.x, to_kill.y], size)
  2269.                      Else Begin
  2270.                             garbage := Life (board.groups [count]^, player, board, depth, to_kill);
  2271.                             If on_board (to_kill.x, to_kill.y)
  2272.                               Then Inc (value [to_kill.x, to_kill.y], size)
  2273.                           End;
  2274.             Inc (count)
  2275.           End;
  2276.       move := highest_rated (value)
  2277.     End;  {Attack Opponent}
  2278.  
  2279.  
  2280.   PROCEDURE Push (var board: board_type; player: color_type; var move: location_list; var weights: integer_board; depth: byte);
  2281.     {Plays on opponent's side of borders}
  2282.     Var
  2283.       x, y: byte;
  2284.       value: integer_board;
  2285.     Begin  {Push}
  2286.       WriteLn ('I just need a few more points...');
  2287.       If player = white_stone
  2288.         Then For x := 1 to board_size do
  2289.                For y := 1 to board_size do
  2290.                  weights [x, y] := - weights [x, y];
  2291.       For x := 1 to board_size do
  2292.         For y := 1 to board_size do
  2293.           If (weights [x, y] < 0) and border (x, y, weights)
  2294.                and legal_move (x, y, player, board) and not suicidal (x, y, player, board)
  2295.             Then value [x, y] := terrain [x, y] + weights [x, y]
  2296.             Else value [x, y] := 0;
  2297.       move := highest_rated (value)
  2298.     End;  {Push}
  2299.  
  2300.  
  2301.   PROCEDURE Invade (var board: board_type; player: color_type; var move: location_list;
  2302.                       var weights: integer_board; depth: byte);
  2303.     {Plays inside loose enemy territory}
  2304.     Var
  2305.       x, y: byte;
  2306.       value: integer_board;
  2307.     Begin  {Invade}
  2308.       WriteLn ('Geronimo!');
  2309.       If player = white_stone
  2310.         Then For x := 1 to board_size do
  2311.                For y := 1 to board_size do
  2312.                  weights [x, y] := - weights [x, y];
  2313.       For x := 1 to board_size do
  2314.         For y := 1 to board_size do
  2315.           If (weights [x, y] < 0) and maneuvering_room (x, y, player, board)
  2316.                and legal_move (x, y, player, board) and not suicidal (x, y, player, board)
  2317.             Then value [x, y] := terrain [x, y] + weights [x, y]
  2318.             Else value [x, y] := 0;
  2319.       move := highest_rated (value)
  2320.     End;  {Invade}
  2321.  
  2322.  
  2323.   PROCEDURE Seal_Up (var board: board_type; player: color_type; var move: location_list;
  2324.                        var weights: integer_board; depth: byte);
  2325.     {Plays on own side of borders}
  2326.     Var
  2327.       x, y: byte;
  2328.       value: integer_board;
  2329.     Begin  {Seal Up}
  2330.       WriteLn ('I don''t have to be greedy.');
  2331.       If player = white_stone
  2332.         Then For x := 1 to board_size do
  2333.                For y := 1 to board_size do
  2334.                  weights [x, y] := - weights [x, y];
  2335.       For x := 1 to board_size do
  2336.         For y := 1 to board_size do
  2337.           If (weights [x, y] > 0) and border (x, y, weights)
  2338.                and legal_move (x, y, player, board) and not suicidal (x, y, player, board)
  2339.             Then value [x, y] := (maxint - weights [x, y]) + terrain [x, y]
  2340.             Else value [x, y] := 0;
  2341.       move := highest_rated (value)
  2342.     End;  {Seal Up}
  2343.  
  2344.  
  2345. END.  {Tactics}
  2346. SHAR_EOF
  2347. fi
  2348. if test -f 'trickle.fum'
  2349. then
  2350.     echo shar: "will not over-write existing file 'trickle.fum'"
  2351. else
  2352. cat << \SHAR_EOF > 'trickle.fum'
  2353. +1
  2354. 3
  2355. 3
  2356.  
  2357. SHAR_EOF
  2358. fi
  2359. exit 0
  2360. #    End of shell archive
  2361.